package LatentTicTacToe.Calculators;

import java.util.LinkedList;

import LatentTicTacToe.Data.Move;
import LatentTicTacToe.InformationSets.CFRInformationSet;

public class LTTTProbabilityCalculator extends ProbabilityCalculator{
	
	public LTTTProbabilityCalculator() {
	}

	@Override
	public void updateProbabilities(int iteration) {
		updateProbabilities("0", null, null);		
	}
	
	/**
	 * Updates probability in all information sets
	 * 
	 * @param id
	 * @param probability
	 */
	private void updateProbabilities(String id, LinkedList<Move> moves, Move lastMove) {

		CFRInformationSet set = gameTree.get(id);
		CFRInformationSet grandParent;
		CFRInformationSet grandGrandParent;
		String grandParentId = getGrandParentId(id);
		String grandGrandParentId = getGrandGrandParentId(id);
		Move move;

		if (grandGrandParentId == null) {
			set.setProbability(1);
		} else {

			grandParent = gameTree.get(grandParentId);
			grandGrandParent = gameTree.get(grandGrandParentId);
			move = getLastMove(getParentId(id));

			if (set == null) {	
				handleSetWithMultipleTerminals(id, moves, set, grandParent, grandGrandParent, move);
				return;
			}

			if (set.winner != 0) {
				updateTerminalSetProbability(id, set);
				return;
			}

			set.setProbability(grandParent.getProbability() * grandGrandParent.getStrategyForMove(move));
		}

		if (lastMove != null && set.winner == 0) {
			updateSingleStateProbability(addMoveToId(id, lastMove.toString()));
		}

		for (Move successor : set.moves) {
			updateProbabilities(addMoveToId(id, successor.toString()), set.moves, successor);
		}

	}

	private void handleSetWithMultipleTerminals(String id, LinkedList<Move> moves, CFRInformationSet set, CFRInformationSet grandParent, CFRInformationSet grandGrandParent, Move move) {
		for (Move possibleLastMove : moves) {
			set = gameTree.get(addMoveToId(id, possibleLastMove.toString()));
			set.setProbability(grandParent.getProbability() * grandGrandParent.getStrategyForMove(move));
		}
	}

	private void updateTerminalSetProbability(String id, CFRInformationSet set) {
		
		String tempGrandParentId = getGrandParentId(id);
		String tempGrandGrandParentId = getGrandGrandParentId(id);
		CFRInformationSet grandParent = gameTree.get(tempGrandParentId.substring(0, tempGrandParentId.length() - 3));
		CFRInformationSet grandGrandParent = gameTree.get(tempGrandGrandParentId.substring(0, tempGrandGrandParentId.length() - 3));

		set.setProbability(grandParent.getProbability() * grandGrandParent.getStrategyForMove(getLastMove(getParentId(id))));
	}

	private void updateSingleStateProbability(String id) {

		CFRInformationSet peer = gameTree.get(getSameLevelId(id));
		CFRInformationSet parent = gameTree.get(getParentId(id));
		CFRInformationSet set = gameTree.get(id);
		Move move = getLastMove(id);

		if (parent == null) {
			set.setProbability(gameTree.get("").getStrategyForMove(move));
			return;
		}

		set.setProbability(peer.getProbability() * parent.getStrategyForMove(move));

	}
	
	private String addMoveToId(String id, String move) {

		StringBuilder builder = new StringBuilder();

		builder.append(id);
		builder.append(".");
		builder.append(move);

		return builder.toString();
	}


	private String getSameLevelId(String id) {
		return id.substring(0, id.length() - 6);
	}

	private String getGrandParentId(String id) {

		if (id == null) {
			return null;
		}

		if (id.length() == 4) {
			return "";
		}

		if (id.length() < 7) {
			return null;
		}

		if (arelastTwoMovesEqual(id)) {

			if (id.length() == 10) {
				return "";
			}

			if (id.length() < 12) {
				return null;
			}
			return id.substring(0, id.length() - 12);
		}

		return id.substring(0, id.length() - 6);

	}

	private String getGrandGrandParentId(String id) {
		return getParentId(getGrandParentId(id));
	}

	private String getParentId(String id) {

		if (id == null) {
			return null;
		}

		if (id.length() == 1) {
			return "";
		}

		if (id.length() < 4) {
			return null;
		}

		if (arelastTwoMovesEqual(id)) {

			if (id.length() == 7) {
				return "";
			}

			if (id.length() < 9) {
				return null;
			}

			return id.substring(0, id.length() - 9);
		}

		return id.substring(0, id.length() - 3);

	}

	private Move getLastMove(String id) {

		byte x;
		byte y;

		if (id.length() < 3) {
			return null;
		}

		x = Byte.parseByte(id.substring(id.length() - 2, id.length() - 1));
		y = Byte.parseByte(id.substring(id.length() - 1, id.length()));

		return new Move(x, y);
	}

	private boolean arelastTwoMovesEqual(String id) {
		if (id.length() < 7) {
			return false;
		}
		return id.substring(id.length() - 2, id.length()).equals(id.substring(id.length() - 5, id.length() - 3));
	}

}
